<?php
/**
/*  vim:ts=4:expandtab:cindent
 *  This file is part of Urd.
 *
 *  Urd is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *  Urd is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program. See the file "COPYING". If it does not 
 *  exist, see <http://www.gnu.org/licenses/>.
 * 
 * $LastChangedDate: 2009-01-03 23:44:12 +0100 (Sat, 03 Jan 2009) $
 * $Rev: 410 $
 * $Author: gavinspearhead $
 * $Id: do_functions.php 410 2009-01-03 22:44:12Z gavinspearhead $
 */

if (!defined('ORIGINAL_PAGE')) die('This file cannot be accessed directly.');

$pathdf = realpath(dirname(__FILE__));


require_once "$pathdf/../functions/autoincludes.php";
require_once "$pathdf/../functions/urdversion.php";
require_once "$pathdf/../functions/defines.php";
require_once "$pathdf/../config.php";
require_once "$pathdf/../functions/functions.php";
require_once "$pathdf/urdd_command.php";
require_once "$pathdf/urdd_protocol.php";
require_once "$pathdf/urdd_error.php";
require_once "$pathdf/../functions/urd_log.php";
require_once "$pathu/../functions/mail_functions.php";


function parse_nzb_line(DatabaseConnection $db, $line, $dlid, array &$data, &$count, &$totalsize)
{
	assert(is_numeric ($count) && is_numeric($totalsize));
	$status = DOWNLOAD_READY;
	// Get poster and subject:
	if (preg_match("/<file.* subject=\"([^\"]+)\"/", $line, $result))
		$data['subject'] = $result[1];

	if (preg_match("/<file.* poster=\"([^\"]+)\"/", $line, $result))
		$data['poster'] = $result[1];

	// Get group:
	if (preg_match('/<group>(.*)<\/group>/', $line, $result)) {
		$group = $result[1];
		$db->escape($group);
		$res =  $db->select_query("\"ID\" FROM groups WHERE \"name\" LIKE '$group'", 1);
		if (!is_array($res)) {
			// We don't have the group in the grouplist.
			$groupID = 0; // Does this really matter? Is it used for downloading? // yes I think it is
		} else 
			$groupID = $res[0]['ID'];
		$data['groupid'] = $groupID;	
	}

	// Get file data:
	if (preg_match("/<segment.* bytes=\"([^\"]+)\"/", $line, $result))
		$data['bytes'] = $result[1];

	if (preg_match("/<segment.* number=\"([^\"]+)\"/", $line, $result))
		$data['number'] = $result[1];

	if (preg_match("/<segment[^>]*>([^<]+)<\/segment>/", $line, $result))
		$data['messageID'] = $result[1];
	// Add to 'downloadarticles':
	if (isset($data['subject']) && isset($data['poster']) && isset($data['messageID']) && isset($data['number']) && isset($data['bytes']) && isset($data['groupid'])) {
		// todo: If download not yet created, create it now (earlier means empty dl if this fails)
		$messageID = html_entity_decode($data['messageID']);
		$cleansubject = $data['subject'];
		$totalsize += $data['bytes'];
		$binaryID = create_binary_id($cleansubject, $data['poster']);

		$cols = array('downloadID', 'groupID', 'partnumber', 'name', 'status', 'messageID', 'binaryID', 'size');
		$vals = array($dlid, $data['groupid'], $data['number'], $cleansubject, $status, $messageID, $binaryID, $data['bytes']);

		$res = $db->insert_query('downloadarticles', $cols, $vals);
		if ($count == 0 && get_download_name($db, $dlid) == '') {
			$dlname = find_name($db, $data['subject']);
            set_download_name($db, $dlid, $dlname);	
        }
        unset($data['messageID']);
        $count++;
    }
}


function do_parse_nzb(DatabaseConnection $db, action $item)
{
    echo_debug_function(DEBUG_SERVER, __FUNCTION__);
    $args = $item->get_args();
    $arg_list = split_args($args);
    $url = $arg_list[1];
    $dlid = $arg_list[0];
    $dlname = basename($url, '.nzb');
    $dlname = find_name($db, $dlname);
    if (get_download_name($db, $dlid) == '') {
        if (strlen($dlname) <= 5 || is_numeric($dlname))
            set_download_name($db, $dlid, 'NZB imported download ' . time());
        else
            set_download_name($db, $dlid, $dlname);
    }
    $file = @fopen ($url, 'r');
    if ($file === FALSE) {
        write_log("Could not read file $url", LOG_ERR);
        $status = QUEUE_FAILED;
        update_dlinfo_status($db, DOWNLOAD_ERROR, $dlid);
        update_queue_status($db, $item->get_dbid(), $status, 0, 100, 'Importing NZB file failed');
        dec_dl_lock($db, $dlid);
        if (check_last_lock($db, $dlid)) // we also set a lock on create so we need to remove that too
            dec_dl_lock($db, $dlid);
        return FILE_NOT_FOUND; 
    }
    $count = $totalsize = 0;
    $data = array();
    $fs = 0;
    while(!feof($file)) {
        $line = fgets($file);
        $fs += count($line);
        parse_nzb_line($db, $line, $dlid, $data, $count, $totalsize);
    }
    echo_debug("Parsed $count lines, total size in NZB $totalsize bytes", DEBUG_SERVER);
    set_download_size($db, $dlid, $totalsize);
    add_stat_data($db, STAT_ACTION_IMPORTNZB, $fs, $item->get_username());
    if ($count == 0) {
        $status = QUEUE_FAILED;
        update_queue_status($db, $item->get_dbid(), $status, 0, 100, 'Imported NZB failed: no articles found');
        $retval = FILE_NOT_FOUND;
    } else {
        $status = QUEUE_FINISHED;
        update_queue_status($db, $item->get_dbid(), $status, 0, 100, 'Imported NZB file');
        $retval = NO_ERROR;
    }
    $dlpath = get_dlpath($db) . SPOOL_PATH;
    if (substr($url, 0, strlen($dlpath)) == $dlpath && substr($url, -11) == '.processing') {
        $new_name = find_unique_name(substr($url, 0, strlen($url) - 11), '', '', '.processed', TRUE);
        rename($url, $new_name);
    }

    dec_dl_lock($db, $dlid);

    if (check_last_lock($db, $dlid)) // we also set a lock on create so we need to remove that too
        dec_dl_lock($db, $dlid);
    return $retval;
}


function write_binary(DatabaseConnection $db, $binaryid, $groupid, $totalParts, $subject, $file, $dlid)
{
    assert (is_resource($file) && is_numeric($groupid));
    echo_debug_function(DEBUG_SERVER, __FUNCTION__);
    $db->escape($binaryid, TRUE);
    $res = $db->select_query("\"active\" FROM groups WHERE \"ID\"=$groupid", 1);
    if ($res === FALSE || $res[0]['active'] != NG_SUBSCRIBED)
        $res = FALSE;
    else 
        $res = $db->execute_query ("SELECT *, \"date\" AS unixdate FROM binaries_$groupid WHERE \"binaryID\" = $binaryid");
    $group = group_name($db, $groupid);
    if ($res === FALSE) {
        $bin_data['subject'] = $subject;
        $bin_data['totalParts'] = $totalParts;
        $bin_data['unixdate'] = time();
    } else 
        $bin_data = $res[0];

    $str = '';
    $totalParts = $bin_data['totalParts'];
    $date = $bin_data['unixdate'];
    $name = preg_replace("/[^a-zA-Z0-9\(\)\! .]/", '', str_replace('"', '', $bin_data['subject']));
    $str .= "\t<file poster=\"who@no.com\" date=\"$date\" subject=\"$name (1/{$totalParts})\">\n";
    $str .= "\t<groups>\n";
    $str .= "\t\t<group>{$group}</group>\n";
    $str .= "\t</groups>\n";
    $str .= "\t<segments>\n";
    $res2 = $db->execute_query ("SELECT \"ID\", \"messageID\", \"partnumber\", \"size\" FROM downloadarticles WHERE \"binaryID\" = $binaryid AND \"downloadID\" = $dlid ORDER BY \"partnumber\"");
    if ($res2 === FALSE) $res2 = array();
    foreach ($res2 as $arr2) {
        $messageID = $arr2['messageID'];
        $partnumber = $arr2['partnumber'];
        $messageID = str_replace('&', '&amp;', $messageID);
        $size = $arr2['size'];
        $str .= "\t\t<segment bytes=\"$size\" number=\"".round($partnumber)."\">$messageID</segment>\n";
    }
    update_batch($db, $res2, DOWNLOAD_COMPLETE);
    $str .= "\t</segments>\n";
    $str .= "\t</file>\n";
    return fwrite ($file, $str);
}


function do_make_nzb(DatabaseConnection $db, action $item)
{
    echo_debug_function(DEBUG_SERVER, __FUNCTION__);
    static $lock_array = array('downloadarticles'=>'write', 'groups'=>'read'); // must lock groups too...
    $odlid = $dlid = $item->get_args();
    if (check_dl_lock($db, $dlid) === FALSE) { // if db still locked
        echo_debug('Dl still locked, sleeping', DEBUG_SERVER); // todo needs fixing
        return DB_LOCKED;
    }
    $compression = (bool) get_config($db, 'compress_nzb'); 
    $dlpath = $item->get_dlpath();
    $file_name = get_download_name($db, $dlid);
    $file_name = sanitise_download_name($db, $file_name);
    $nzb_file = find_unique_name($dlpath, '', $file_name, '.nzb', TRUE);
    $basename = $file_name;
    $fn = $nzb_file;
    $ext = '.nzb';
    try {
        if ($compression === TRUE) {
            $gzip = get_config($db, 'gzip_path');
            $gzip_pars = get_config($db, 'gzip_pars');
            if ($gzip == '' || !file_exists($gzip)) {
                write_log("File not found $gzip", LOG_ERR);
                $compression = FALSE;
            }
        }
    } catch (exception $e) {
        $compression = FALSE;
        write_log($e->getMessage(), LOG_ERR);
    }
    if ($compression === TRUE) {
        $cmd = "/bin/sh -c '$gzip $gzip_pars ' ";
        $zip_file = find_unique_name($dlpath, '', $file_name, '.nzb.gz', TRUE);
        $fn = $zip_file;
        $ext = '.nzb.gz';
        $descriptorspec = array ( 
            0 => array('pipe', 'r'), // where we will write to
            1 => array('file', $zip_file, 'w'), // we don't want the output
            2 => array('file', '/dev/null', 'w') // or the errors
            //		1 => array('file', '/tmp/out', 'w'), // we don't want the output
            //		2 => array('file', '/tmp/err', 'w') // or the errors
        );
        $pipes = array();
        $process = proc_open($cmd, $descriptorspec, $pipes, $dlpath, NULL, array('binary_pipes'));
        $file = $pipes[0];
    }
    else 
        $file = fopen($nzb_file, 'w+');
	if ($file === FALSE)
		throw new exception ('Could not create file: '  .$nzb_file, FILE_NOT_CREATED); // XXX
	$size = 0;
	$str = '<' . '?' . 'xml version="1.0" encoding="us-ascii"' . '?' . '>' . "\n"; // screws up syntax highlighting... hence the wacky sequence of < and ? and ? and > ... DO NOT CHANGE!
	$str .= '<!DOCTYPE nzb PUBLIC "-//newzBin//DTD NZB 1.0//EN" "http://www.newzbin.com/DTD/nzb/nzb-1.0.dtd">'. "\n";
	$str .= '<nzb xmlns="http://www.newzbin.com/DTD/2003/nzb">' . "\n";
	$str .= '<!-- Created by URD: http://www.urdland.com : The web-based usenet resource downloader. -->' . "\n";
	$size += fwrite($file, $str);

	$db->lock($lock_array);
	$db->escape($dlid, TRUE);
	try {
		$query = "\"binaryID\", count(*) AS cnt, max(\"groupID\") AS \"groupID\", max(\"name\") AS subject FROM downloadarticles WHERE \"downloadID\"=$dlid GROUP BY \"binaryID\" ORDER BY \"binaryID\"";
		$res = $db->select_query($query);
        if ($res === FALSE) 
            throw new exception('Could not find any articles', INTERNAL_FAILURE);
		$total_count = count($res);
		$counter = 0;
		$status = QUEUE_RUNNING;
		foreach ($res as $binary){
			$size += write_binary($db, $binary['binaryID'], $binary['groupID'], $binary['cnt'], $binary['subject'], $file, $dlid);
			$counter++;
			update_queue_status($db, $item->get_dbid(), $status, 0, floor(($counter/$total_count) * 100));
		}
	} catch (exception $e) {
		$db->unlock();
		throw $e;
	}
	$db->unlock();

	$str = '</nzb>' . "\n";
    $size += fwrite($file, $str);
    if ($compression === TRUE) {
        pclose($file);
        $rv = proc_close($process);
    } else {
        fclose($file);
    }
    $username =  $item->get_username();
    $done = move_file_to_nzb($db, $odlid, $fn, $dlpath, $basename, $ext, $username);
    set_permissions($db, $done); // setting  permissions must be last otherwise we may not be able to move the file
    set_group($db, $done); 

	$status = QUEUE_FINISHED;
	update_queue_status($db, $item->get_dbid(), $status, 0, 100, 'Created NZB file');
	delete_download_article($db, $odlid, DOWNLOAD_COMPLETE);
	delete_download_article($db, $odlid, DOWNLOAD_FAILED); // needed? we never set failed
	add_stat_data($db, STAT_ACTION_GETNZB, $size, $username);
	return NO_ERROR;
}


function create_make_nzb(DatabaseConnection $db, $servers, $username, $priority)
{
	assert (is_numeric($priority));
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
	global $responses;
	$dl_path_basis = get_dlpath($db);
	$dl_path = $dl_path_basis . TMP_PATH . $username . '/';
	clearstatcache();
	if (!is_dir($dl_path)) {
		$rv = @mkdir($dl_path, 0775, TRUE);
		if ($rv === FALSE) {
			write_log("Failed to create directory $dl_path", LOG_ERR);
			return $responses[405];
		}		
	}
	if (!is_writeable($dl_path)) {
		$rv = @chmod($dl_path, 0775); // sometimes mkdir doesn't set the perms correctly (due to umask??), make sure it is set correctly now
		if ($rv === FALSE)
			write_log("Can't chmod directory: $dl_path", LOG_ERR);
	}

	if (!is_writable($dl_path)) {
		write_log("Download directory is not writable: $dl_path", LOG_ERR);
		return $responses[405];
	}
	$status = DOWNLOAD_READY;
	$id = add_download($db, $username, 0, 0, 0, 0, $status, '', DLTYPE_NZB);
	$item = new action(COMMAND_MAKE_NZB, $id, $username, TRUE);
	$item->set_dlpath($dl_path);
    set_download_dir($db, $id, $dl_path);
	$res = $servers->queue_push($db, $item, TRUE, QUEUE_BOTTOM, $priority);
	if ($res === FALSE) 
		throw new exception ('Could not queue item', ERR_QUEUE_FAILED);
	update_queue_norestart($db, $res);
	$id_str = "[{$item->get_id()}] ";
	return sprintf($responses[210], $id, $id_str);
}


function do_priority(DatabaseConnection $db, array $arg_list, server_data &$servers, $username)
{
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
	global $responses;

	$priority = $arg_list[1];
	$id = $arg_list[0];
	if (!is_numeric($priority) || !is_numeric($id))
		return $responses[501];
	if ($priority <= 1)
		return $responses[501];
	try {
		$rv = $servers->queue_set_priority($db, $id, $username, $priority);
		return $responses[200];
	} catch (exception $e) {
		return $responses[501];
	}
}


function store_extset_data(DatabaseConnection $db, array $result, array &$setidarray)
{
	$counter = 0;
	foreach ($result as $row) {
		$setid = $row[0];
		$name = $row[1];
		$value = substr($row[2], 0, 255);
		$type = $row[3];
		// rewrite old names
		if ($name == 'moviescore' || $name == 'musicscore')
			$name = 'score';
		if (in_array($name, array('movielink', 'musiclink', 'serielink')))
			$name = 'link';
        if ($name == 'MERGE_SET') {
            $db->merge_sets($value, array($setid));
        } else {
            $setidarray[$setid] = '1';

            $db->escape($setid, TRUE);
            $db->escape($name, TRUE);
            $db->escape($value, TRUE);
            $db->escape($type, TRUE);

            // Insert or update?
            $sql = "SELECT \"setID\" FROM extsetdata WHERE \"setID\" = $setid AND \"name\" = $name AND \"type\" = $type";
            $res = $db->execute_query($sql);

            if (is_array($res)) {// Exists, so update:
                $sql = "UPDATE extsetdata SET \"value\" = $value WHERE \"setID\" = $setid AND \"name\" = $name AND \"type\" = $type";
            } else {
                $sql = "INSERT INTO extsetdata (\"setID\", \"name\", \"value\", \"committed\", \"type\") VALUES ($setid, $name, $value, 1, $type)";
            }
            $db->execute_query($sql);
        }
        $counter++;
	}
	return $counter;
}


function regenerate_setnames(DatabaseConnection $db, array $setidarray)
{
	foreach ($setidarray as $setid => $spam) {
		$db->escape($setid, TRUE);
		$sql = "SELECT \"name\", \"value\", \"type\" FROM extsetdata WHERE \"setID\" = $setid";
		$res = $db->execute_query($sql);
		if ($res === FALSE)
			continue;
		// Convert to proper format:
		$namevalues = array();
		$type = $res[0]['type'];
		foreach ($res as $row)
			$namevalues[$row['name']] = $row['value'];
		$setname = generate_set_name($db, $namevalues);
		$db->escape($setname, TRUE);

		// Setname in db?
		if (isset($namevalues['setname'])) {
			$sql = "UPDATE extsetdata SET \"value\" = $setname WHERE \"setID\" = $setid AND \"name\" = 'setname' AND \"type\"=$type";
		} else {
			$sql = "INSERT INTO extsetdata (\"setID\", \"name\", \"value\", \"committed\", \"type\") VALUES ($setid, 'setname', $setname, 1, $type)";
		}
		$db->execute_query($sql);
	}
}


function update_group_timestamp(DatabaseConnection $db, $name, $timestamp)
{
	$db->escape($name, TRUE);
	$db->escape($timestamp, TRUE);
    $sql = "UPDATE groups SET \"extset_update\" = $timestamp WHERE \"name\" LIKE $name";
	$db->execute_query($sql);
}


function update_feed_timestamp(DatabaseConnection $db, $name, $timestamp)
{
	$db->escape($name, TRUE);
	$db->escape($timestamp, TRUE);
	$sql = "UPDATE rss_urls SET \"extset_update\" = $timestamp WHERE \"url\" LIKE $name";
	$db->execute_query($sql);
}


function do_getsetinfo (DatabaseConnection $db, action $item)
{
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
	write_log('Getting extsetdata', LOG_INFO);

	$prefs = load_config($db);
	$userinfo = array($prefs['forumusername'], $prefs['forumpassword']);

	// We ask for setinfo for specific groups within specific period, so we need groupname and expire time (in days):
	$sql = 'SELECT "name", "expire", "extset_update" FROM groups WHERE "active" = 1';
	$groupinfo = $db->execute_query($sql);

	$sql = 'SELECT "url" AS "name", "expire", "extset_update" FROM rss_urls WHERE "subscribed" = 1';
	$feedinfo = $db->execute_query($sql);
	$now = time();
	$feedgroupinfo = array(USERSETTYPE_GROUP => $groupinfo, USERSETTYPE_RSS=>$feedinfo);
	$counter = 0;
	$grp_cnt = count($feedinfo) + count($groupinfo);
	$cnt = 0;
	$setidarray = array();
	foreach ($feedgroupinfo as $type => $info) {
        if (!is_array($info))
            continue;
		foreach($info as $src) {

			$params = array($userinfo, array($src));

			$result = XMLRPC_to_URD_central_server('GetSetInfo', $params);

			if (!is_array($result)) {
				write_log('Could not receive extsetdata.', LOG_INFO);
				$status = QUEUE_FAILED;
				$comment = 'Error while receiving extsetdata, see log for info';
				update_queue_status($db, $item->get_dbid(), $status, NULL, 100, $comment);
				return URDLAND_CONNECT_ERROR;
			}

			write_log('Extsetdata received', LOG_INFO);
			$status = QUEUE_RUNNING;
			$comment = 'Received info, updating database';
			$cnt ++;
			update_queue_status($db, $item->get_dbid(), NULL, NULL, floor(80 * ($cnt/$grp_cnt)), $comment);

			// Keep track of changed setid's, to regenerate the setname:

			$tmp_cnt = store_extset_data($db, $result, $setidarray);
			$counter += $tmp_cnt;
			write_log("Extsetdata processed (For {$src['name']} $tmp_cnt tags downloaded)", LOG_INFO);
			$status = QUEUE_RUNNING;
			$comment = 'Database updated, cleaning up';
			update_queue_status($db, $item->get_dbid(), NULL, NULL, floor(80 * (($cnt + 0.5)/$grp_cnt)), $comment);
			if ($type == USERSETTYPE_GROUP)
				update_group_timestamp($db, $src['name'], $now);
			else if ($type == USERSETTYPE_RSS)
				update_feed_timestamp($db, $src['name'], $now);
			else 
				write_log('Unknown type of information', LOG_ERR);

		}
	}
	// regenerate set names:
	//
	regenerate_setnames($db, $setidarray);

    $status = QUEUE_FINISHED;
	update_queue_status($db, $item->get_dbid(), $status, 0, 100, "Done, downloaded $counter tags");

	return NO_ERROR;
}


function send_setinfo(DatabaseConnection $db, array $setinfo, array $userinfo)
{
	$params = array($userinfo, $setinfo);
	$result = XMLRPC_to_URD_central_server('SendSetInfo', $params);

	// If everthing is OK, the server returns true.
	if ( $result === FALSE ) {
		return FALSE ;	
	}
	$cnt = count($setinfo);
	write_log("Extsetdata sent $cnt items", LOG_INFO);
	return TRUE;
}


function do_sendsetinfo (DatabaseConnection $db, action $item)
{
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);

	write_log('Sending extsetdata', LOG_INFO);

	// Prepare querys for stuff to be sent:
	$sql1 = " groups.\"name\" AS groupname, extsetdata.\"setID\", extsetdata.\"name\", extsetdata.\"value\", extsetdata.\"type\" FROM extsetdata ";
	$sql1 .= "LEFT JOIN setdata ON extsetdata.\"setID\" = setdata.\"ID\" LEFT JOIN groups ON setdata.\"groupID\" = groups.\"ID\" ";
	$sql1 .= "WHERE extsetdata.\"committed\" = 0 AND extsetdata.\"name\" != 'setname' AND extsetdata.\"type\" = '" . USERSETTYPE_GROUP . "'";

	$sql2 = " rss_urls.\"url\" AS groupname, extsetdata.\"setID\", extsetdata.\"name\", extsetdata.\"value\", extsetdata.\"type\" FROM extsetdata ";
	$sql2 .= "LEFT JOIN rss_sets ON extsetdata.\"setID\" = rss_sets.\"setid\" LEFT JOIN rss_urls ON rss_sets.\"rss_id\" = rss_urls.\"id\" ";
	$sql2 .= "WHERE extsetdata.\"committed\" = 0 AND extsetdata.\"name\" != 'setname' AND extsetdata.\"type\" = '" . USERSETTYPE_RSS . "'";

    $sql3 = " groups.\"name\" AS groupname, merged_sets.\"old_setid\" AS \"setID\", 'MERGE_SET' AS \"name\", merged_sets.\"new_setid\" AS value, merged_sets.\"type\" FROM merged_sets ";
	$sql3 .= "JOIN setdata ON merged_sets.\"new_setid\" = setdata.\"ID\" LEFT JOIN groups ON setdata.\"groupID\" = groups.\"ID\" ";
	$sql3 .= "WHERE merged_sets.\"committed\" = 0 AND merged_sets.\"type\" = '" . USERSETTYPE_GROUP . "'";
    
	$sql_cnt1 = " COUNT(*) AS \"counter\" FROM extsetdata ";
	$sql_cnt1 .= "LEFT JOIN setdata ON extsetdata.\"setID\" = setdata.\"ID\" LEFT JOIN groups ON setdata.\"groupID\" = groups.\"ID\" ";
	$sql_cnt1 .= "WHERE extsetdata.\"committed\" = 0 AND extsetdata.\"name\" != 'setname' AND extsetdata.\"type\" = '" . USERSETTYPE_GROUP . "'";

	$sql_cnt2 = " COUNT(*) AS \"counter\" FROM extsetdata ";
	$sql_cnt2 .= "LEFT JOIN rss_sets ON extsetdata.\"setID\" = rss_sets.\"setid\" LEFT JOIN rss_urls ON rss_sets.\"rss_id\" = rss_urls.\"id\" ";
	$sql_cnt2 .= "WHERE extsetdata.\"committed\" = 0 AND extsetdata.\"name\" != 'setname' AND extsetdata.\"type\" = '" . USERSETTYPE_RSS . "'";
    
    $sql_cnt3 = " COUNT(*) AS \"counter\" FROM merged_sets ";
	$sql_cnt3 .= "LEFT JOIN setdata ON merged_sets.\"old_setid\" = setdata.\"ID\" LEFT JOIN groups ON setdata.\"groupID\" = groups.\"ID\" ";
	$sql_cnt3 .= "WHERE merged_sets.\"committed\" = 0  AND merged_sets.\"type\" = '" . USERSETTYPE_GROUP . "'";

	$res1 = $db->select_query($sql_cnt1);
	$sql_arr[0] = array($sql1, $res1[0]['counter']);
	$res2 = $db->select_query($sql_cnt2);
	$sql_arr[1] = array($sql2, $res2[0]['counter']);
	$res3 = $db->select_query($sql_cnt3);
	$sql_arr[2] = array($sql3, $res3[0]['counter']);
	if ($res1 === FALSE && $res3 === FALSE && $res2 === FALSE) return NO_ERROR;
	$prefs = load_config($db);
	$userinfo = array($prefs['forumusername'], $prefs['forumpassword']);
	$counter = 0;
	$total = $sql_arr[0][1] + $sql_arr[1][1] + $sql_arr[2][1];
	$status = QUEUE_RUNNING;
    $step = 200;
	foreach ($sql_arr as $sql) {
		$start = 0;
		$cnt = $sql[1];
		if ($cnt == 0) {
			write_log('No extsetdata to send.', LOG_INFO);
			continue;
		}
		while ($start <= $cnt) {
			$setinfo = $db->select_query($sql[0], $step, $start);
			if (!is_array($setinfo)) 
                break;
			if (!send_setinfo($db, $setinfo, $userinfo)) {
				write_log('Extsetdata sending failed.', LOG_INFO);
				$status = QUEUE_FAILED;
				$comment = 'Sending setinfo went wrong, check log for info';
				update_queue_status($db, $item->get_dbid(), $status, NULL, 100, $comment);
				return URDLAND_CONNECT_ERROR;
			}
			$start += $step;

			update_queue_status($db, $item->get_dbid(), $status, 0, floor(100 * (($counter+$start)/$total)) , "Done, sent $counter tags.");
		}
		$counter += $sql[1];
	}

	// Finish up:
	write_log('Extsetdata sending succeeded.', LOG_INFO);
	$sql = 'UPDATE extsetdata SET "committed" = 1 WHERE "committed" = 0';
	$res = $db->execute_query($sql);
	$sql = 'UPDATE merged_sets SET "committed" = 1 WHERE "committed" = 0';
	$res = $db->execute_query($sql);

	$status = QUEUE_FINISHED;
	update_queue_status($db, $item->get_dbid(), $status, 0, 100, "Done, sent $counter tags.");
	return NO_ERROR;
}


function do_check_version(DatabaseConnection $db, action $item)
{
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
	$version = file_get_contents(VERSION_CHECK_URL. '?version=' . get_version());
	list ($vstr) = explode("\n", $version, 2);
	$rv = preg_match ('/^(\d+\.\d+\.\d+)[ \t]+(\d)+[ \t]+(.*)$/', $vstr, $matches);
	if ($rv === 0) {
		$status = QUEUE_FAILED;
		$comment = '';
		update_queue_status($db, $item->get_dbid(), $status, NULL, 100, $comment);
		return COULD_NOT_CHECK_VERSION;
	}
	$version = $matches[1];
	$type = $matches[2];
	$text = $matches[3];
	echo_debug("Version: $version", DEBUG_SERVER);
	echo_debug("Type: $type", DEBUG_SERVER);
	echo_debug("Text: $text", DEBUG_SERVER);
	set_pref($db, 'update_version', $version, 0);
	set_pref($db, 'update_type', $type, 0);
	set_pref($db, 'update_text', $text, 0);
	$comment = "Newest version is $version";
	$status = QUEUE_FINISHED;
	update_queue_status($db, $item->get_dbid(), $status, NULL, 100, $comment);
	return NO_ERROR;
}


function update_rss_status(DatabaseConnection $db, $id, $last_updated)
{
	$db->escape($last_updated, TRUE);
	$db->escape($id, TRUE);
	$qry = "UPDATE \"rss_urls\" SET \"last_updated\"=$last_updated WHERE \"id\"=$id ";
	$db->execute_query($qry);
}


function do_update_rss(DatabaseConnection $db, action $item)
{
	echo_debug_function(DEBUG_WORKER, __FUNCTION__);
	try {
		$args = $item->get_args();
		$rss_info = get_rss_info($db, $args);
		$name = $rss_info['name'];
		rss_update($db, $rss_info);
		$cnt = update_rss_status($db, $args, time());
		$status = QUEUE_FINISHED;
		update_queue_status($db, $item->get_dbid(), $status, NULL, 100);
		mark_interesting_rss($db, $args);
	} catch (exception $e) {
		$status = QUEUE_FAILED;
		update_queue_status($db, $item->get_dbid(), $status, NULL, 100, $e->getMessage());
		write_log("RRS Update $name failed: " .$e->getmessage(), LOG_ERR);
		return FEED_NOT_FOUND;
	}
	return NO_ERROR;
}


function do_update(DatabaseConnection $db, action $item)
{
	echo_debug_function(DEBUG_WORKER, __FUNCTION__);

	$args = $item->get_args();
	try {
        $groupArr = get_group_info($db, $args);
	} catch (exception $e) {
		$status = QUEUE_FAILED;
		update_queue_status($db, $item->get_dbid(), $status, NULL, 100, 'Group not found');
		throw new exception('Error: group not found', ERR_GROUP_NOT_FOUND);
	}
	try { 
		global $config;
		$server_id = $item->get_active_server();
		if ($server_id == 0)
            $server_id = $item->get_preferred_server();
        $sql = "SELECT \"retention\", \"compressed_headers\" FROM usenet_servers WHERE \"id\"='$server_id'";
        $rv = $db->execute_query($sql);
        $retention = $rv[0]['retention'];
        $compressed_headers = $rv[0]['compressed_headers'];
        $groupArr['retention'] = $retention; // ugly code really
        $groupArr['compressed_headers'] = $compressed_headers; // ugly code really
		$nzb = connect_nntp($db, $server_id);
        $rv = $nzb->update_newsgroup($groupArr, $item);
		if ($rv === ERR_GROUP_NOT_FOUND) {
			$status = QUEUE_FAILED;
			update_queue_status($db, $item->get_dbid(), $status, 0, 100);
			return GROUP_NOT_FOUND;
		}
		if ($rv !== TRUE) {
			$nzb->disconnect();
			return NNTP_NOT_CONNECTED_ERROR;
		}
        $status = QUEUE_FINISHED;
        $db->update_postcount($groupArr['ID']);
		update_queue_status($db, $item->get_dbid(), $status, 0, 100);
		$nzb->disconnect();
	} catch (exception $e) {
		if ($e->getcode() == ERR_GROUP_NOT_FOUND){
			return GROUP_NOT_FOUND;
		} else
			return NNTP_NOT_CONNECTED_ERROR;
	}
	return NO_ERROR;
}


function do_listupdate(DatabaseConnection $db, action $item)
{
	echo_debug_function(DEBUG_WORKER, __FUNCTION__);
	$rv = NO_ERROR;
	$comment = '';
	try {
		$server_id = $item->get_active_server();
		if ($server_id == 0)
            $server_id = $item->get_preferred_server();
		$nzb = connect_nntp($db, $server_id);
		list($update, $insert) = $nzb->update_group_list(get_config($db, 'default_expire_time'));
		$nzb->disconnect();
		$comment = "$update updated and $insert new groups";
		$status = QUEUE_FINISHED;
		$rv = NO_ERROR;
	} catch (exception $e) {
		write_log('connection failed', LOG_ERR);
		$status = QUEUE_FAILED;
		$rv = NNTP_NOT_CONNECTED_ERROR;
	}
	update_queue_status($db, $item->get_dbid(), $status, 0, 100, $comment);
	return $rv;
}


function do_gensets(DatabaseConnection $db, action $item)
{		
		echo_debug_function(DEBUG_WORKER, __FUNCTION__);
		$args = $item->get_args();
		$do_expire = (get_config($db, 'auto_expire') == 1);
		try {
				$groupArr = get_group_info($db, $args);
		} catch (exception $e) {
				$status = QUEUE_FAILED;
				update_queue_status($db, $item->get_dbid(), $status, NULL, 100, 'Group not found');
				throw new exception('Error: group not found', ERR_GROUP_NOT_FOUND);
		}
		$groupname = $groupArr['name'];
        $expire = time() - ($groupArr['expire'] * 24 * 3600);
        if (get_config($db, 'auto_expire') == 1)
            $db->quick_expire($groupArr['ID']);
		try {
				$db->update_binary_info($args, $groupname, $do_expire, $expire, $item);
        } catch (exception $e) {
				write_log('update Binary info failed', LOG_ERR);
		}
		update_queue_status($db, $item->get_dbid(), NULL, 0, 99, 'Marking sets');
		try {
				mark_interesting($db, $args);
		} catch (exception $e) {
				write_log('mark_interesting failed ' . $e->getMessage(), LOG_ERR);
		}
		update_user_last_seen_group($db, $args);
		$status = QUEUE_FINISHED;
		update_queue_status($db, $item->get_dbid(), $status, 0, 100, 'Generated sets data complete');
		return NO_ERROR;
}


function do_expire_rss(DatabaseConnection $db, action $item)
{		
	echo_debug_function(DEBUG_WORKER, __FUNCTION__);
	$args = $item->get_args();
	// Check if group exists
	$res = FEED_subscribed($db, $args);
	if ($res === FALSE) {
		$status = QUEUE_FAILED;
		update_queue_status($db, $item->get_dbid(), $status, NULL, 100, 'Feed not found');
		return FEED_NOT_FOUND;
	}
	$count = expire_rss($db, $args, $item->get_dbid());
	$status = QUEUE_FINISHED;
	update_queue_status($db, $item->get_dbid(), $status, 0, 100, "Deleted $count articles");
	return NO_ERROR;
}


function do_expire(DatabaseConnection $db, action $item)
{		
	echo_debug_function(DEBUG_WORKER, __FUNCTION__);
	$args = $item->get_args();
	// Check if group exists
	$res = group_subscribed($db, $args);
	if ($res === FALSE) {
		$status = QUEUE_FAILED;
		update_queue_status($db, $item->get_dbid(), $status, NULL, 100, 'Group not found');
		return GROUP_NOT_FOUND;
	}
	$count = $db->expire_binaries($args, $item->get_dbid());
	$status = QUEUE_FINISHED;
	update_queue_status($db, $item->get_dbid(), $status, 0, 100, "Deleted $count articles");
	return NO_ERROR;
}


function do_cleandb(DatabaseConnection $db, action $item)
{
	echo_debug_function(DEBUG_WORKER, __FUNCTION__);
	global $responses;
	$finished_status = DOWNLOAD_FINISHED;
	$failed_status = DOWNLOAD_FAILED;
	$rar_failed = DOWNLOAD_RAR_FAILED;
	$par_failed = DOWNLOAD_PAR_FAILED;
    $cksfv_failed = DOWNLOAD_CKSFV_FAILED;
    $cancelled_status = DOWNLOAD_CANCELLED;
	$post_finished_status = POST_FINISHED;
	$post_rar_failed_status = POST_RAR_FAILED;
	$post_par_failed_status = POST_PAR_FAILED;
    $post_cancelled_status = POST_CANCELLED;

    $db->escape($cancelled_status, TRUE);
	$db->escape($finished_status, TRUE);
	$db->escape($rar_failed, TRUE);
	$db->escape($par_failed, TRUE);
	$db->escape($cksfv_failed, TRUE);
	$db->escape($failed_status, TRUE);

	try {
		$arg = strtolower($item->get_args());
		if ($arg == 'all') { // do not use truncate as that will reset the autoinc values too
			$db->execute_query('TRUNCATE downloadinfo ');
			$db->execute_query('TRUNCATE downloadarticles ');
			$db->execute_query('TRUNCATE queueinfo ');
			$db->execute_query('TRUNCATE postinfo ');
		} else {
			// Delete all info that has occurred (ended) <clean_db_age> days ago.
			// For example, downloads that finished 3 days ago, queue messages that happened 3 days ago etc.
			if ($arg == 'now') {
				$timebased = FALSE;
				$cleandbage = 1;
			} else if (is_numeric($arg) && $arg > 0) {
				$timebased = TRUE;
				$cleandbage = $arg * 24 * 60 * 60;
			} else {
				$timebased = TRUE;
				$cleandbage = get_config($db, 'clean_db_age') * 24 * 60 * 60; // db age is saved in days... convert to seconds
			}
			if ($cleandbage > 0) {// 0 = disabled
				// Clean up downloadinfo:
				if ($timebased)
					$timestamp = time() - $cleandbage;
				else
					$timestamp = time();
				$qry = "DELETE FROM downloadinfo WHERE \"start_time\" < $timestamp "
                    . " AND (\"status\" = $finished_status OR \"status\" = $rar_failed OR \"status\" = $par_failed OR \"status\" = $cksfv_failed " 
                    . " OR \"status\" = $cancelled_status OR \"status\" = $failed_status) ";
				$db->execute_query($qry);
                
                $qry = "DELETE FROM postinfo WHERE \"start_time\" < $timestamp "
                    . " AND (\"status\" = $post_finished_status OR \"status\" = $post_rar_failed_status OR \"status\" = $post_par_failed_status" 
                    . " OR \"status\" = $post_cancelled_status) ";
				$db->execute_query($qry);

				// Clean up queueinfo:
				$qry = "DELETE FROM queueinfo WHERE \"lastupdate\" < $timestamp AND (\"status\" = '" . QUEUE_FINISHED . "' OR \"status\" = '" . QUEUE_FAILED 
					. "' OR \"status\" = '" . QUEUE_CRASH . "' OR \"status\" = '" . QUEUE_CANCELLED . "' OR \"status\" = '" . QUEUE_REMOVED . "')" ;
				$db->execute_query($qry);
			}

			// Clean downloadarticles from any lost records:
			$qry = 'DELETE FROM downloadarticles WHERE "downloadID" NOT IN (SELECT "ID" FROM downloadinfo)';
			$db->execute_query($qry);
		}
	} catch (exception $e) {
		write_log('Database query failed: ' . $e->getmessage(), LOG_WARNING);
		$status = QUEUE_FAILED;
		update_queue_status($db, $item->get_dbid(), $status, 0, 100, $e->getMessage());
		return INTERNAL_FAILURE;
	}
	$status = QUEUE_FINISHED;
	update_queue_status($db, $item->get_dbid(), $status, 0, 100, 'Cleaned Database');
	return NO_ERROR;
}


function do_group(DatabaseConnection $db, $servers, array $arg_list, $username)
{
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
	global $responses;
	if (is_numeric($arg_list[0])) {
		$groupId = $arg_list[0];
		try {
			$response = $responses[258];
			$server_id = $servers->get_update_server();
			$nzb = connect_nntp($db, $server_id);
			list($first, $last, $count) = $nzb->get_first_last_group($groupId);
			$nzb->disconnect();
			$response .= "$first $last $count\n";
			$response .= ".\n";

		} catch (exception $e) {
			write_log($e->getMessage(), LOG_WARNING);
			$response = $responses[520];
		}
	} else 
		$response = $responses[501];
	return $response;
}


function do_cleandir(DatabaseConnection $db, action $item)
{
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
	global $responses;
	$dlpath = get_dlpath($db);
	$tmp_dir = $dlpath . TMP_PATH;
	$nzb_dir = $dlpath . NZB_PATH;
	$post_dir = $dlpath . POST_PATH;
	$preview_dir = $dlpath . PREVIEW_PATH;
	$age = get_config($db, 'clean_dir_age') * 24 * 60 * 60; // db age is saved in days... convert to seconds
	$preview = $tmp = $nzb = $post = FALSE;
	$arg = $item->get_args();
	$cnt1 = $cnt2 = $cnt3 = $cnt4 = 0;
	switch (strtolower($arg)) {
	case 'all':
		$preview = $tmp = TRUE; // only throw away preview files and temp files, leave nzb and post untouched; only remove those if specifically asked to.
		break;
	case 'preview' :
		$preview = TRUE;
		break;
	case 'tmp':
		$tmp = TRUE;
		break;
	case 'nzb':
        $nzb = TRUE;
    case 'post':
        $post = TRUE;
	default:
		$status = QUEUE_FAILED;
		update_queue_status($db, $item->get_dbid(), $status, NULL, 100, 'Incorrect argument');
		return COMMANDLINE_ERROR;
	}
	$error = '';
	try {
		if ($tmp === TRUE) 
			list ($cnt1, $error) = rmdirtree($tmp_dir, $age, FALSE);
		if ($nzb === TRUE) {
			list ($cnt3, $error3) = rmdirtree($nzb_dir, $age, FALSE);
			$error .=  "\n" .$error3;
		}
		if ($post === TRUE) {
			list ($cnt4, $error4) = rmdirtree($nzb_dir, $age, FALSE);
			$error .=  "\n" .$error4;
		}
		if ($preview === TRUE) {
			list ($cnt2, $error2) = rmdirtree($preview_dir, $age, FALSE);
			$error .=  "\n" .$error2;
		}
		write_log("Removed $cnt1 tmp files, $cnt3 nzb files, $cnt4 post files and $cnt2 preview files", LOG_INFO);
		$error = trim($error, "\n\r\t ");
		if ($error == '') {
			$status = QUEUE_FINISHED;
			update_queue_status($db, $item->get_dbid(), $status, NULL, 100, 'Deleted files');
			return NO_ERROR;
		} else {
			$status = QUEUE_FAILED;
			update_queue_status($db, $item->get_dbid(), $status, NULL, 100, 'Not all files could be deleted');
			write_log($error, LOG_ERR);
			return COMMANDLINE_ERROR;
		}
	} catch (exception $e) {
		$status = QUEUE_FAILED;
		update_queue_status($db, $item->get_dbid(), $status, NULL, 100, 'Something happened that should not');
		write_log($e->getMessage(), LOG_WARNING);
		return COMMANDLINE_ERROR;
	}
	$status = QUEUE_FINISHED;
	update_queue_status($db, $item->get_dbid(), $status, NULL, 100, 'Deleted files');
	return NO_ERROR;
}


function do_purge_rss(DatabaseConnection $db, action $item)
{		
	echo_debug_function(DEBUG_WORKER, __FUNCTION__);
	$args = $item->get_args();
	// Check if the group exists
	$res = feed_subscribed($db, $args);
	if ($res === FALSE) {
		$status = QUEUE_FAILED;
		update_queue_status($db, $item->get_dbid(), $status, NULL, 100, 'Feed not found');
		return FEED_NOT_FOUND;
	}
	purge_rss($db, $args, $item->get_dbid());
	$status = QUEUE_FINISHED;
	update_queue_status($db, $item->get_dbid(), $status, 0, 100, 'Finished');
	return NO_ERROR;
}


function do_purge(DatabaseConnection $db, action $item)
{		
	echo_debug_function(DEBUG_WORKER, __FUNCTION__);
	$args = $item->get_args();
	// Check if the group exists
	$res = group_subscribed($db, $args);
	if ($res === FALSE) {
		$status = QUEUE_FAILED;
		update_queue_status($db, $item->get_dbid(), $status, NULL, 100, 'Group not found');
		return GROUP_NOT_FOUND;
	}
	$db->purge_binaries($args);
	$status = QUEUE_FINISHED;
	update_queue_status($db, $item->get_dbid(), $status, 0, 100, 'Finished');
	return NO_ERROR;
}


function do_diskfree(DatabaseConnection $db, $format, $username)
{
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
	$dlpath = get_dlpath($db);
	$df = disk_free_space($dlpath);
	$dt = disk_total_space($dlpath);
	$msg = 'available';
	switch($format[0]){
	case 'P': 
		// override some values
		$df = $dt - $df; // used space instead of free space
		$msg = 'used'; 
		// fall thru
	case 'p':		
		$dec = 0;
		if(isset($format[1])) 
			$dec = substr($format, 1);
		$perc = round ($df / $dt * 100, $dec);
		return array(263, "$perc % $msg\n");
		break;
	default:
		list($df, $tf) = format_size($df, $format);
		list($dt, $tt) = format_size($dt, $format);

		return array(256, "$df $tf of $dt $tt\n");
		break;
	}
}


function do_adddata(DatabaseConnection $db, $item)
{
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
	try {
		$args = preg_split('/[\s]+/', $item->get_args());

		$dlid = $args[0];
		$res = $db->execute_query("SELECT * FROM downloadinfo WHERE \"ID\" = '$dlid'");
		if($res === FALSE) {
			throw new exception('Invalid download ID'); // other error TODO
		}
		$type = $args[1];
		switch (strtolower($type)) {
		case 'set':
			$setid = $args[2];
			$status = DOWNLOAD_READY;
			$res = $db->execute_query("SELECT * FROM setdata WHERE \"ID\" = '$setid'");
			if($res === FALSE) 
				throw new exception('Invalid Set ID');
			
			$groupid = $res[0]['groupID'];
			$size = $res[0]['size'];

			$parts = 'parts_' . $groupid;
			$binaries = 'binaries_' . $groupid;
			$sql = "INSERT INTO downloadarticles (\"downloadID\", \"groupID\", \"status\", \"partnumber\", \"name\", \"messageID\", \"binaryID\", \"size\") "
				. "SELECT '$dlid', '$groupid', '$status', \"partnumber\", bin.\"subject\", \"messageID\", par.\"binaryID\", par.\"size\" FROM $parts AS par "
                . "LEFT JOIN $binaries AS bin ON (bin.\"binaryID\" = par.\"binaryID\") WHERE bin.\"setID\" = '$setid'";
            $res2 = $db->execute_query($sql);

            $sql = " \"value\" FROM extsetdata WHERE \"setID\" = '$setid' AND \"name\"= 'password'";
            $res3 = $db->select_query($sql, 1);
            if (isset($res3[0]['value'])) {
                $pw = $res3[0]['value'];
                $db->escape($pw, TRUE);
                $sql = "UPDATE downloadinfo SET \"password\" = $pw WHERE \"ID\" = '$dlid' AND \"password\" = ''";
                $db->execute_query($sql);
            }

			dec_dl_lock($db, $dlid);
			break;	
		case 'binary':
			$groupid = $args[2];
			$binid = $args[3];
			$parts = 'parts_' . $groupid;
			$status = DOWNLOAD_READY;
			$binaries = 'binaries_' . $groupid;
			$db->escape($binid, TRUE);
			// need to rewrite to insert_query XXX
			$sql = "INSERT INTO downloadarticles (\"downloadID\", \"groupID\", \"status\", \"partnumber\", \"name\", \"messageID\", \"binaryID\", \"size\") "
				.  "SELECT '$dlid', '$groupid', '$status', \"partnumber\", bin.\"subject\", \"messageID\", $binid, par.\"size\" FROM $parts AS par "
				.  "LEFT JOIN $binaries AS bin ON (bin.\"binaryID\" = par.\"binaryID\") WHERE bin.\"binaryID\" = $binid";
			$res2 = $db->execute_query($sql);

			dec_dl_lock($db, $dlid);
			break;
		case 'article':
			throw new exception ('Not implemented yet');
			break;
		}
	} catch (exception $e) {
		$status = QUEUE_FAILED;
		echo_debug($e->getMessage(), DEBUG_SERVER);
		update_queue_status($db, $item->get_dbid(), $status, 0, 100, 'Finished');
		dec_dl_lock($db, $dlid);
		throw $e;
	}
    update_dlinfo_groupid($db, $dlid, $groupid);
	$status = QUEUE_FINISHED;
	update_queue_status($db, $item->get_dbid(), $status, 0, 100, 'Finished');
	if (check_last_lock($db, $dlid)) // we also set a lock on create so we need to remove that too
		dec_dl_lock($db, $dlid);

	return NO_ERROR;
}


function do_set(DatabaseConnection $db, server_data &$servers, array $arg_list, $username)
{
	global $responses, $config, $log_str, $yes, $no;
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
	if(!is_admin($db, $username))
		return $responses[532];
	switch(strtoupper($arg_list[0])) {
	case 'LOG_LEVEL' : 
		$res = array_search (strtoupper($arg_list[1]), $log_str);
		if ( $res !== FALSE) {
			$config['urdd_min_loglevel'] = $res;
			set_pref($db, 'log_level', $res, 0);
			$response = $responses[200];
		} else 
			$response = $responses[501];
		break;
	case 'SCHEDULER':
		if (in_array(strtoupper($arg_list[1]), $yes)) {
			$config['scheduler'] = TRUE;
			set_pref($db, 'scheduler', 'on', 0);
			$response = $responses[200];
		} else if (in_array(strtoupper($arg_list[1]), $no)) {
			$config['scheduler'] = FALSE;
			set_pref($db, 'scheduler', 'off', 0);
			$response = $responses[200];
		} else 
			$response = $responses[501];
        break;
	case 'SERVER':
        try {
			if (isset($arg_list[1]) && is_numeric($arg_list[1])  && isset($arg_list[2]) && is_numeric($arg_list[2])) {
				$id = $arg_list[1];
				$prio = $arg_list[2];
				if ($prio == 0) {
					$servers->disable_server($id);
					disable_usenet_server($db, $id);
				} else {
					$servers->enable_server($id, $prio);
					enable_usenet_server($db, $id, $prio);
				}
				$response = $responses[200];
			} else if (isset($arg_list[1]) && ($arg_list[1] == 'reload')  && isset($arg_list[2]) && is_numeric($arg_list[2])) {
				$servers->reload_server($db, $arg_list[2]);
				$response = $responses[200];
			} else if (isset($arg_list[1]) && ($arg_list[1] == 'load')  && isset($arg_list[2]) && is_numeric($arg_list[2])) {
				$servers->load_server($db, $arg_list[2]);
				$response = $responses[200];
			} else if (isset($arg_list[1]) && ($arg_list[1] == 'delete')  && isset($arg_list[2]) && is_numeric($arg_list[2])) {
				$servers->delete_server($arg_list[2]);
				$response = $responses[200];
			} else if (isset($arg_list[1]) && ($arg_list[1] == 'posting')  && isset($arg_list[2]) && is_numeric($arg_list[2])) {
				$servers->enable_posting($arg_list[2]);
                set_posting($db, $arg_list[2], TRUE);
				$response = $responses[200];
            } else if (isset($arg_list[1]) && ($arg_list[1] == 'noposting')  && isset($arg_list[2]) && is_numeric($arg_list[2])) {
                set_posting($db, $arg_list[2], FALSE);
				$servers->disable_posting($arg_list[2]);
				$response = $responses[200];
			} else 
				$response = $responses[501];
		} catch (exception $e) {
			echo_debug_trace($e, DEBUG_SERVER);
			$response = $responses[501];
		}
		break;
	case 'PREFERRED':
		try {
			if (isset($arg_list[1]) && is_numeric($arg_list[1])) {
				$id = $arg_list[1];
				set_config($db, 'preferred_server', $id);
				$servers->set_update_server($id);
				$response = $responses[200];
			} else 
				$response = $responses[501];
		} catch (exception $e) {
			write_log($e->getMessage(), LOG_WARNING);
			$response = $responses[501];
		}
		break;
	default:
		$response = $responses[501];
		break;
	}
	return $response;
}


function do_download(DatabaseConnection $db, action $item)
{
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
	return start_download($item, $db);
}


function do_optimise(DatabaseConnection $db, action $item)
{
	try {
		$fetch_mode = $db->set_fetch_mode(ADODB_FETCH_NUM);
		$res = $db->get_tables(); 
		$total_rows = count($res);
		$cntr = $mtime = 0;
		$stime = get_microtime();
		$status = QUEUE_RUNNING;
		update_queue_status($db, $item->get_dbid(), NULL, 0, 0);
		foreach ($res as $row)  {
			write_log("Optimising table {$row[0]}", LOG_INFO);
			$perc = ceil(((float)$cntr * 100) / $total_rows);
			if ($perc != 0)
				$time_left = ($mtime - $stime)  * (($total_rows - $cntr) / $cntr) ;
			else 
				$time_left = 0;
			update_queue_status($db, $item->get_dbid(), $status, $time_left , $perc, "Optimising {$row[0]}");
			$db->optimise_table($row[0]); 
			$mtime = get_microtime();
			$cntr++;
		}
		$status = QUEUE_FINISHED;
		$db->set_fetch_mode($fetch_mode); 
		update_queue_status($db, $item->get_dbid(), $status, 0, 100, 'Done');
		return NO_ERROR;
	} catch (exception $e) {
		$db->set_fetch_mode($fetch_mode);
		write_log($e->getMessage(), LOG_WARNING);
		return DB_FAILURE;
	}
}


function do_unschedule(DatabaseConnection $db, array $arg_list, server_data  &$server, $username)
{
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
	global $responses;
	$rv = FALSE;
	$arg = strtoupper ($arg_list[0]);
	$e_username = get_effective_username($db, $username);
	try {
		if (is_numeric($arg))
			$rv = $server->unschedule($db, $e_username, $arg);
		else if (strtolower($arg) == 'all')
			$rv = $server->unschedule_all($db, $e_username);
		else if (match_command($arg) !== FALSE) {
			$cmd = $arg_list[0];
			$arg = isset($arg_list[1]) ? $arg_list[1] : '';
			$rv = $server->unschedule_cmd($db, $e_username, $cmd, $arg);
		} else	
			return $responses[501];
	} catch (exception $e) {
		write_log($e->getMessage(), LOG_WARNING);
		return $responses[532];
	}
	return ($rv === TRUE) ?  ($responses[200]) : ($responses[511]);
}


function do_schedule(DatabaseConnection $db, array $arg_list, server_data &$servers, $username)
{
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
	global $responses;
	$cmd = trim(strtoupper($arg_list[0]));
	if (match_command($cmd) === FALSE)
		return $responses[501];
	if ($arg_list[1][0] != '@')
		$arg = $arg_list[1];
	else 
		$arg = '';

	// format is:
	// schedule "command arguments" @ start_time # recurrence in seconds
	// with the "" optional and the # as well.
	$args = implode(" ", $arg_list);
	$parts = explode("@", $args);
	$cmds = trim ($parts[0]);
	if (!isset($parts[1]))
		return $responses[501];	
	$parts = explode("#", $parts[1]);
	$stime = trim($parts[0]);
	if (isset($parts[1]))
		$repeat = trim($parts[1]);
	else 
		$repeat = NULL;
	$stime = strtotime($stime);
	if ($stime === FALSE) 
		return $responses[521];
	if ($repeat !== NULL && !is_numeric($repeat)) 
		return $responses[522];
	$item = new action ($cmd, $arg, $username, FALSE);
	$now = time();
	if (($repeat  !== NULL) && ($now > $stime)) {
		while ($now > $stime) 
			$stime += $repeat;
	} else if ($repeat === NULL && $now > $stime) 
		$stime += 24 * 60 * 60; // next day

	$servers->add_schedule($db, new job($item, $stime, $repeat));
	return sprintf($responses[201], $item->get_id());
}


function do_pause(DatabaseConnection $db, server_data &$servers, array $arg_list, $pause, $username)
{
	assert(is_bool($pause));
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
	global $responses;
	try {
		$e_username = get_effective_username($db, $username);
		if (is_numeric($arg_list[0])) {
			$id = $arg_list[0];
			try {
				$rv = $servers->pause($db, $id, $pause, $e_username);
			} catch (exception $e) {
				$rv = FALSE;
			}
			$response = ($rv === FALSE)? $responses[510]: $responses[200];
        } else if (match_command($arg_list[0]) !== FALSE) {
			$cmd = $arg_list[0];
			$arg = '';
			if (isset ($arg_list[1])) 
				$arg = $arg_list[1];
			try {
				$rv = $servers->pause_cmd($db, $cmd, $arg, $pause, $e_username);
			} catch (exception $e) { 
				$rv = FALSE;
			} 
			$response = ($rv === FALSE)? $responses[510]: $responses[200];
		} else if (strtolower($arg_list[0]) == 'all'){
			$servers->pause_all($db, $pause, $e_username);
			$response = $responses[200];
		} else 
			$response = $responses[501];
	} catch (exception $e) {
		write_log($e->getMessage(), LOG_WARNING);
		$response = $responses[532];
	}
	return $response;
}


function do_status(server_data $servers, conn_list $conn_list)
{
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
	global $start_time;
	$uptime = time () - $start_time;
	$sec = $uptime % 60;
	$min = ($uptime / 60) % 60;
	$hrs = ($uptime / 3600) % 24;
	$days = (int) ($uptime / (3600 * 24));
	$status = "Uptime: $days days $hrs:$min:$sec\n";
	$qs = $servers->queue_size();
	$ts = $servers->threads_size();
	$ss = $servers->schedule_size();
	$us = $conn_list->size();
	$status .= "Queued: $qs\n";
	$status .= "Running: $ts\n";
	$status .= "Scheduled: $ss\n";
	$status .= "Users: $us\n";
	return $status;
}


function do_preempt(DatabaseConnection $db, server_data &$servers, array $arg_list, $username)
{
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
	global $responses;
	$response = '';
	$e_username = get_effective_username($db, $username);
	if (isset($arg_list[0]) && is_numeric($arg_list[0])) {
		try {
			$id = $arg_list[0];
			if (isset($arg_list[1]) && is_numeric($arg_list[1])) 
				$servers->stop($db, $arg_list[1], $e_username);
			$rv = $servers->move_top($db, $id, $e_username);
			$response_move = ($rv === FALSE)? $responses[510]: $responses[200];

		} catch (exception $e) {
			$response =  $responses[501];
		}

	} else 
		$response = $responses[501];
	return $response;
}


function do_stop(DatabaseConnection $db, server_data &$servers, array $arg_list, $username)
{
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
	global $responses;
	$e_username = get_effective_username($db, $username);
	if (is_numeric($arg_list[0])) {
		$id = $arg_list[0];
		try {
			$rv = $servers->stop($db, $id, $e_username);
		} catch (exception $e) {
			if ($e->getCode() == ERR_ACCESS_DENIED)
				throw $e;
			$rv = FALSE;
		}
		$response = ($rv === FALSE)? $responses[510]: $responses[200];
	} else if (strtolower($arg_list[0]) == 'all') {
		$servers->stop_all($db, $e_username);
		$response = $responses[200];
	} else 
		$response = $responses[501];
	return $response;
}


function do_cancel(DatabaseConnection $db, server_data &$servers, array $arg_list, $username)
{
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
	global $responses;
	try {
		$e_username = get_effective_username($db, $username);
		if (is_numeric($arg_list[0])) {
			$id = $arg_list[0];
			$rv = $servers->delete($db, $id, $e_username, TRUE);
			$response = ($rv === FALSE)? $responses[510]: $responses[200];
		} else if (match_command($arg_list[0]) !== FALSE) {
			$cmd = $arg_list[0];
			$arg = isset ($arg_list[1]) ? $arg_list[1] : '';
			$rv = $servers->delete_cmd($db, $e_username, $cmd, $arg, TRUE);
			$response = ($rv === FALSE)? $responses[510]: $responses[200];
		} else if (strtolower($arg_list[0]) == 'all') {
			do_cancel_all($db, $servers, $username);
			$response = $responses[200];
		} else 
			$response = $responses[501];
	} catch (exception $e) {
		write_log($e->getMessage(), LOG_WARNING);
		$response = $responses[532];
	}
	return $response;
}


function do_cancel_all(DatabaseConnection $db, server_data &$servers, $username, $do_kill= FALSE)
{
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
	assert(is_bool($do_kill));
	$isadmin = (is_admin($db, $username) || ($do_kill === TRUE));
	$servers->delete_all($db, $isadmin?'root':$username, !$do_kill);
}


function do_subscribe_rss(DatabaseConnection $db, array $arg_list, server_data &$servers, $username)
{
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
	global $responses;
	try {
		$def_exp = get_config($db, 'default_expire_time');
		if (isset($arg_list[0]) && is_numeric($arg_list[0])) {
			$id = $arg_list[0];
            $is_subscribed = feed_subscribed($db, $id);
			if (isset($arg_list[1]) && strtolower($arg_list[1]) == 'on' && $is_subscribed === TRUE) {
				if (isset($arg_list[2]) && is_numeric($arg_list[2])) {
					$exp = $arg_list[2];
					set_feed_expire($db, $id, $exp);
				}
			} else if (isset($arg_list[1]) && strtolower($arg_list[1]) == 'on' && $is_subscribed === FALSE) {
				if (isset($arg_list[2]) && is_numeric($arg_list[2]))
					$exp = $arg_list[2];
				else
					$exp = $def_exp;
				update_feed_state($db, $id, RSS_SUBSCRIBED, $exp);

				$response = queue_update_rss($db, COMMAND_UPDATE_RSS, array($arg_list[0]), $username, $servers);
				return $response;
			} else	if (strtolower($arg_list[1]) == 'off' && $is_subscribed !== FALSE) {
				purge_rss($db, $id);
				update_feed_state($db, $id, RSS_UNSUBSCRIBED, $def_exp);
				$response = $responses[200];
				return $response;
			}
		}
	} catch (exception $e) {
		write_log($e->getMessage(), LOG_WARNING);
		return sprintf($responses[503], $e->getMessage());
	}
	return $responses[501];
}


function do_subscribe(DatabaseConnection $db, array $arg_list, server_data &$servers, $username)
{
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
	global $responses;
	try {
		if (isset($arg_list[0]) && is_numeric($arg_list[0])) {
			$id = $arg_list[0];
			$is_subscribed = group_subscribed($db, $id);
			if (isset($arg_list[1]) && strtolower($arg_list[1]) == 'on' && $is_subscribed === TRUE) {
				if (isset($arg_list[2]) && is_numeric($arg_list[2])) {
					$exp = $arg_list[2];
					set_group_expire($db, $id, $exp);
				}
			} else if (isset($arg_list[1]) && strtolower($arg_list[1]) == 'on' && $is_subscribed === FALSE) {
				if (isset($arg_list[2]) && is_numeric($arg_list[2]))
					$exp = $arg_list[2];
				else
					$exp = get_config($db, 'default_expire_time');
				$db->subscribe($id, $exp);
				$response = queue_update($db, COMMAND_UPDATE, array($arg_list[0]), $username, $servers);
				return $response;
			} else	if (strtolower($arg_list[1]) == 'off' && $is_subscribed !== FALSE) {
				$res = $db->unsubscribe($id);
				$db->purge_binaries($id);
				if ($res)
					$response = $responses[200];
				else 
					$response = $responses[402];
				return $response;
			}
		}
	} catch (exception $e ) {
		write_log($e->getMessage(), LOG_WARNING);
		return sprintf($responses[503], $e->getMessage());
	}
	return $responses[501];
}


function do_unpar_unrar(DatabaseConnection $db, action $item)
{
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
	$args = explode (' ', $item->get_args());
    $dlid = $args[0];
    $query = "\"unpar\", \"unrar\", \"subdl\", \"delete_files\", \"destination\", \"first_run\" FROM downloadinfo WHERE \"ID\"=$dlid";
    $res = $db->select_query($query, 1);
        if ($res === FALSE) {
        $unpar = $concat = $delete_files = $first_run = $dl_subs = 0;
    } else {
        $unpar = $res[0]['unpar'];
        $concat = $unrar = $res[0]['unrar'];
        $delete_files = $res[0]['delete_files'];
        $first_run = $res[0]['first_run'];
        $dl_subs = $res[0]['subdl'];
    }
    update_dlinfo_status($db, DOWNLOAD_COMPLETE, $dlid);
	$to = $item->get_dlpath();
	$dl_status = NULL;
	$all_files = array();
	$uu_error = $par_error = $concat_error = $comp_error = $dir_error = $sfv_error = FALSE;
	$comment = '';
	$username = $item->get_username();
	if (!is_dir($to)) {
		$status = QUEUE_FAILED;
		write_log("Not a directory: $to", LOG_ERR);
		$cksfv = $unpar = $unrar = $uudecode = $concat = 0;
		$files = array();
		$dir_error = TRUE;
	} else {
		$files = get_par_rar_files($db, $to, $all_files);
		$uudecode = 1;
	}
	if ($uudecode > 0) {
		$counter = 0;
		$comment .= uudecode($db, $files, $dlid, $to, $uu_error, $dl_status, $counter);
		if ($uu_error === TRUE) {
			$cksfv = $unpar = $unrar = $uudecode = 0;
		} else if ($counter > 0) {
			if ($delete_files > 0)
				$files->delete_files(FALSE, FALSE, FALSE, TRUE);
			$files = get_par_rar_files($db, $to, $all_files); // re-read the file list, as uuencode creates new ones
		}
	}
	if ($unpar > 0) {
		$comment .= verify_par($db, $to, $dlid, $files, $item, $par_error, $unpar);
		if ($par_error === TRUE) {
			; //$unrar = 0; // don't unrar if there is a par2 error
		} else {
			if ($delete_files > 0)
				$files->delete_files(TRUE, FALSE, FALSE, FALSE);
		}
	}
	if ($unpar <= 0) {		
		$comment = 'PAR2 not run ';		
		$starttime = time();	
		// but try cksfv

		$comment .= verify_cksfv($db, $to, $dlid, $files, $item, $sfv_error);
		if ($sfv_error === TRUE) {
			$unrar = 0;
		} else {
			if ($delete_files > 0)
				$files->delete_files(FALSE, FALSE, TRUE, FALSE);
		}
	}

	if ($concat > 0) {
		$comment .= concat_files($db, $files, $dlid, $to, $concat_error, $dl_status);
		if ($concat_error !== TRUE && $delete_files > 0)
			$files->delete_files(FALSE, FALSE, FALSE, FALSE, TRUE);
		else {
			if ($par_error === TRUE && $unpar > 0) {
				$par_error = FALSE; // if there is a par2 err, it may be that we first have to concat before parring again
				$comment .= verify_par($db, $to, $dlid, $files, $item, $par_error, $unpar);
				if ($par_error === TRUE) {
					$unrar = 0;
				} else {
					if ($delete_files > 0)
						$files->delete_files(TRUE, FALSE, FALSE, FALSE);
				}
            }
        }
	}
	if ($unrar > 0) {
		global $archives;
		$password = get_password($db, $dlid);
		foreach($archives as $type) {
			$comment .= decompress($db, $type, $to, $files, $password, $dlid, $dl_status, $comp_error_tmp);
			if ($comp_error_tmp === TRUE) 
				$comp_error = TRUE;
		}
		if ($comp_error !== TRUE && $delete_files > 0) 
			$files->delete_files(FALSE, TRUE, FALSE, FALSE);
	} else
		$comment .= ' Decompression not run';
	if (!isset($status) || $status == QUEUE_RUNNING) 
		$status = QUEUE_FINISHED;
	// clean up dir
	if ($delete_files > 0) 
		cleanup_dir($to, $all_files);	

    if ($uu_error === FALSE && $par_error === FALSE && $concat_error === FALSE && $comp_error === FALSE && $dir_error === FALSE && $dl_subs && !$item->get_preview()) {
        download_subs($db, $item, $dlid, $to, $username);
    }
	if (!isset($comment)) 
		$comment = 'Complete';
	echo_debug("download finished $dlid", DEBUG_SERVER);
	update_queue_status($db, $item->get_dbid(), $status, 0, 100, $comment);
    if ($first_run) {
        move_download_to_done($db, $item, $dlid, $username);
    }
    $done = get_download_destination($db, $dlid);
    if ($first_run) {
        set_permissions($db, $done); // setting  permissions must be last otherwise we may not be able to move the file
        set_group($db, $done); 
    }

    if ($uu_error === FALSE && $par_error === FALSE && $concat_error === FALSE && $comp_error === FALSE && $dir_error === FALSE && $sfv_error === FALSE) {
        update_dlinfo_status($db, DOWNLOAD_FINISHED, $dlid);
    }

    $dl_status = get_dlinfo_status($db, $dlid); // various functions set this value, so we must retrieve it from the db
    if ($first_run)
        run_all_scripts($db, $item, $dlid, $dl_status);
    //remove lock file once we won't touch the dir anymore
    if (file_exists($done . URDD_DOWNLOAD_LOCKFILE))
        unlink($done . URDD_DOWNLOAD_LOCKFILE);

    update_dlinfo_firstrun($db, $dlid, FALSE); 
	if (!$item->get_preview()) {
		try {
			mail_user_download($db, $dlid, $item->get_username(), $dl_status);
		} catch (exception $e) {
			write_log('Could not send message', LOG_WARNING);
		}
	}
}


function do_move(DatabaseConnection $db, server_data &$servers, array $arg_list, $username)
{
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
	global $responses;
	$e_username = get_effective_username($db, $username);
	if (isset($arg_list[1]) && (match_command($arg_list[1]) !== FALSE))
		$cmd = $arg_list[1];
	else 
		return $responses[501];
	if (isset($arg_list[2]) && is_numeric($arg_list[2]))
		$id = $arg_list[2];
	else
		return $responses[501];

	if (isset($arg_list[0])) {
		if (strtolower($arg_list[0]) == 'up')
			$direction = MOVE_UP;
		else if (strtolower($arg_list[0]) == 'down')
			$direction = MOVE_DOWN;
		else
			return $responses[501];
	} else 
		return $responses[501];
	try {
		$servers->move($db, $cmd, $id, $e_username, $direction);
	} catch (exception $e) {
		write_log ("Download $id not found", LOG_INFO);
		return $responses[510];
	}

	return $responses[200];
}


function do_merge_sets(DatabaseConnection $db, action $item)
{
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
    //todo
    try {
        $userid = get_userid($db, $item->get_username());
    } catch (exception $e) {
		$status = QUEUE_FAILED;
        update_queue_status($db, $item->get_dbid(), $status, 0, 100, 'User not found');
        return UNKNOWN_ERROR;
    }

    if (!is_seteditor($db, $userid)) {
		$status = QUEUE_FAILED;
        update_queue_status($db, $item->get_dbid(), $status, 0, 100, 'User is no seteditor');
        return NOT_ALLOWED;
    }
    $elems = preg_split('/[\s]+/', $item->get_args());
    $setid1 = $elems[0];
    unset($elems[0]);
    $setid2 = $elems;
    try {
        $db->merge_sets($setid1, $setid2);
    } catch (exception $e) {
		$status = QUEUE_FAILED;
        update_queue_status($db, $item->get_dbid(), $status, 0, 100, 'Merge failed');
        return GROUP_NOT_FOUND;
    }
       
    $status = QUEUE_FINISHED;
	update_queue_status($db, $item->get_dbid(), $status, 0, 100, 'Merged sets');
    return NO_ERROR;
}


function do_find_servers(DatabaseConnection $db, action $item)
{
    $servers = get_server_data($db);
    $test_results =  new test_result_list();
    $arg_list = $item->get_args();

    try { 
        $servers->load_servers($db, $test_results, FALSE);
    } catch (exception $e) {
        if ($e->getcode() == ERR_NO_ACTIVE_SERVER)
            write_log($e->getmessage(), LOG_ERR);
        if ($config ['find_servers'] === FALSE) // if we are gonna run find servers next, we don't want to exit
         ;//   exit ($e->getcode()); // may have to remove this if we want to run find_servers from the website
    }

    $servers->find_servers($db, $test_results, $item->get_dbid(), isset($arg_list[0]) && $arg_list[0] == 'extended', TRUE);
    $status = QUEUE_FINISHED;
	update_queue_status($db, $item->get_dbid(), $status, 0, 100, 'Autoconfigure servers complete');

    return NO_ERROR;
}


function do_delete_set(DatabaseConnection $db, action $item)
{
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
    $arg_list = $item->get_args();
    $setids = preg_split('/[\s]+/', $item->get_args());

    $status = QUEUE_RUNNING;
    $l = count($setids);
    $cnt = 0;
    $cmt = '';
    foreach ($setids as $setid) {
        $db->escape($setid, TRUE);
        $sql = "SELECT \"groupID\" FROM setdata  WHERE \"ID\" = $setid";
        $res = $db->execute_query($sql);
        if ($res === FALSE) {
            write_log("Set not Found $setid", LOG_ERR);
            $cmt .= "Set not Found $setid ";
            continue;
        }
        $group_id = $res[0]['groupID'];
        $sql = "SELECT \"binaryID\" FROM binaries_$group_id WHERE \"setID\" = $setid";
        $res = $db->execute_query($sql);
        if ($res === FALSE)
            $binary_ids = array();
        else
            $binary_ids = $res;

        $sql = "DELETE FROM setdata WHERE \"ID\" = $setid";
        $db->execute_query($sql);

        $sql = "DELETE FROM binaries_$group_id WHERE \"setID\" = $setid";
        $db->execute_query($sql);

        $db->escape($res, TRUE);
        foreach($res as $row) {
            $bin_id = $row['binaryID'];
            $sql = "DELETE FROM parts_$group_id WHERE \"binaryID\" = $bin_id";
            $db->execute_query($sql);
        }
        update_queue_status($db, $item->get_dbid(), $status, 0, (int)(($cnt / $l) * 100), $cmt);
    }
    $status = QUEUE_FINISHED;
	update_queue_status($db, $item->get_dbid(), $status, 0, 100, ($cmt == '' ? 'Complete' : $cmt));
    return NO_ERROR;
}


function do_delete_set_rss(DatabaseConnection $db, action $item)
{
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
    $arg_list = $item->get_args();
    $setids = preg_split('/[\s]+/', $item->get_args());
    $db->escape($setids, TRUE);
    $l = count($setids);
    $status = QUEUE_RUNNING;
    $cnt = 0;
    foreach ($setids as $setid) {
        $sql = "DELETE FROM rss_sets WHERE \"setid\" = $setid";
        $db->execute_query($sql);
        update_queue_status($db, $item->get_dbid(), $status, 0, (int)(($cnt / $l) * 100), 'Complete');
    }
    $status = QUEUE_FINISHED;
	update_queue_status($db, $item->get_dbid(), $status, 0, 100, 'Complete');
    return NO_ERROR;
}


function do_post_message(DatabaseConnection $db, action $item)
{
	echo_debug_function(DEBUG_SERVER, __FUNCTION__);
    $arg_list = $item->get_args();
    $msg_id = $arg_list[0];
    try {
        if (!is_numeric($msg_id)) 
            throw new exception('Message ID not a number');
        $db->escape($msg_id, TRUE);
        $userid = get_userid($db, $item->get_username());
        $db->escape($userid, TRUE);
        $sql = " * FROM post_messages WHERE \"id\" = $msg_id AND \"userid\" = $userid";
        $res = $db->select_query($sql, 1);
        if (!isset($res[0]['id']))
            throw ('Message not found');
        $useragent = get_urd_name() . ' ' . get_version(); 
        $message = $res[0]['message'];
        $groupid = $res[0]['groupid'];
        $group_name = group_name($db, $groupid);
        $subject = $res[0]['subject'];
        $poster_id = $res[0]['poster_id'];
        $poster_name = $res[0]['poster_name'];
        $header = array(
            'from'          => "From: $poster_name <$poster_id>",
            'newsgroups'    => "Newsgroups: $group_name",
            'subject'       => "Subject: $subject" ,
            'user-agent'    => "User-Agent: $useragent",
        );
        $header = implode("\r\n", $header) . "\r\n\r\n";
        $server_id = $item->get_preferred_server();
        $nntp = connect_nntp($db, $server_id);
        $nntp->select_group($groupid, $code);
        $article = wordwrap($message, 70, "\r\n", FALSE);
        $articleid = $nntp->post_article(array($header, $article));
        $nntp->disconnect();
        $status = QUEUE_FINISHED;
        update_queue_status($db, $item->get_dbid(), $status, 0, 100, 'Complete');
    } catch (exception $e) {
        $status = QUEUE_FAILED;
        update_queue_status($db, $item->get_dbid(), $status, 0, 100, 'Failed');
        // and now?
    }
    $sql = "DELETE FROM post_messages WHERE  \"id\" = $msg_id AND \"userid\" = $userid";
    $db->execute_query($sql);
    return NO_ERROR;
}

?>
